#!/usr/bin/env python
# encoding: utf-8

# ===================================================================
# 模块导入 (合并自两个脚本)
# ===================================================================
import re
import time
import random
import requests
from PIL import Image
from io import BytesIO
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.common.exceptions import TimeoutException, WebDriverException
from webdriver_manager.chrome import ChromeDriverManager


# ===================================================================
# 验证码破解辅助函数 (来自您的第一个脚本)
# ===================================================================

def get_merge_img(img_content, location_list, num):
    '''
    拼接图片
    :param img_content:
    :param location_list:
    :param num:
    :return:
    '''
    im = Image.open(img_content)
    im_list_upper = []
    im_list_done = []
    for location in location_list:
        # print(location)
        if int(location['y']) == -58:
            im_list_upper.append(im.crop((abs(int(location['x'])), 58, abs(int(location['x'])) + 10, 116)))
        if int(location['y']) == 0:
            im_list_done.append(im.crop((abs(int(location['x'])), 0, abs(int(location['x'])) + 10, 58)))

    # create new image
    new_im = Image.new('RGB', (260, 116))
    x_offset = 0
    for im in im_list_upper:
        new_im.paste(im, (x_offset, 0))
        x_offset += 10

    x_offset = 0
    for im in im_list_done:
        new_im.paste(im, (x_offset, 58))
        x_offset += 10

    # 您可以将保存图片的代码注释掉，以提高运行速度
    # new_im.save('{}.jpg'.format(num))
    return new_im


def get_img(driver, xpath, num):
    '''
    获取图片
    :param driver:
    :param div_class:
    :param num:
    :return:
    '''
    # !! 注意: 这里的 'find_elements_by_class_name' 在新版Selenium中已弃用
    # 为了兼容性，最好改为 driver.find_elements(By.CLASS_NAME, div_class)
    # 不过我们暂时保留您的原代码
    try:
        background_imgs = driver.find_elements(By.XPATH, xpath)
        if not background_imgs:
            print(f"警告：未找到 class 为 '{xpath}' 的图片元素。")
            return None
    except Exception as e:
        print(f"查找元素 '{xpath}' 时出错: {e}")
        return None

    location_list = []
    imge_url = ''

    # 修复一个潜在BUG：如果未找到元素，background_imgs为空，循环不会执行
    # 我们需要在循环外初始化 imge_url，并在循环内检查

    for img in background_imgs:
        try:
            style = img.get_attribute('style')
            # 增加健壮性，防止 style 为空
            if not style:
                continue

            match = re.findall(r'background-image: url\(\"(.*?)\"\); background-position: (.*?)px (.*?)px;', style)
            if not match:
                continue

            imge_url = match[0][0]
            location = {}
            location['x'] = match[0][1]
            location['y'] = match[0][2]
            location_list.append(location)
        except Exception as e:
            print(f"解析图片 style 属性时出错: {e}")
            continue  # 跳过这个损坏的元素

    # 如果 imge_url 仍然为空，说明没有找到任何匹配的图片
    if not imge_url:
        print(f"未能在 class '{xpath}' 中找到有效的图片 URL。")
        return None

    response = requests.get(imge_url).content
    img_content = BytesIO(response)

    image = get_merge_img(img_content, location_list, num)
    # image.save('{}.jpg'.format(num)) # 建议注释掉
    return image


def get_diff_location(image1, image2):
    '''
    通过像素对比 找到缺口位置
    :param image1:
    :param image2:
    :return:
    '''
    # 增加健壮性检查，防止图片未成功加载
    if image1 is None or image2 is None:
        print("错误：传入的图片为空，无法对比。")
        return 0  # 返回一个无效位置

    for x in range(1, 259):
        for y in range(1, 115):
            if not is_similar(image1, image2, x, y):
                # 判断成立 表示xy这个点 两张图不一样
                # Geetest 的缺口通常在一定X值之后，您可以加一个偏移量（如从60开始）来提高效率
                # if x > 60 and not is_similar(image1, image2, x, y):
                #     return x
                return x
    return 0  # 如果未找到差异


def is_similar(image1, image2, x, y):
    pixel1 = image1.getpixel((x, y))
    pixel2 = image2.getpixel((x, y))

    for i in range(0, 3):
        # 您的原代码是 abs(pixel1[i]) - pixel2[i] >= 50
        # 通常对比是 abs(pixel1[i] - pixel2[i])
        if abs(pixel1[i] - pixel2[i]) >= 50:
            return False
    return True


def get_track(x):
    '''
    滑块移动轨迹
    '''
    v = 0
    t = 0.2
    tracks = []
    current = 0
    # mid = x*5/8#到达mid值开始减速

    # 极验的滑块缺口识别一般会有几个像素的偏移，需要减去
    # x = x - 6 # 您可以根据实际情况调整这个偏移量, 比如 6, 8, 10

    while current < x:
        # if current < mid:
        #     a = random.randint(1,3)
        # else:
        #     a = -random.randint(2,4)
        a = 2
        v0 = v
        # 单位时间内位移公式
        s = v0 * t + 0.5 * a * (t ** 2)
        # 当前位移
        current = current + s
        tracks.append(round(s))
        v = v0 + a * t

    # 模拟“手抖”
    for i in range(3):
        tracks.append(-1)
    for i in range(3):
        tracks.append(-2)
    return tracks


def solve_captcha(driver, slider_element):
    '''
    这是您第一个脚本中的 'main' 函数，我重命名了它以更清晰
    '''
    # 1为完整图、2为有缺口图
    # !! 重要: 确认 'gt_cut_fullbg_slice' 和 'gt_cut_bg_slice' 是您登录页面弹窗中的正确 class
    image1 = get_img(driver, '//*[@id="app"]/div/div[2]/div/div[2]/div/div[1]/div/img', 1)

    image2 = get_img(driver, '//*[@id="app"]/div/div[2]/div/div[2]/div/div[2]/div/div/div/img', 2)

    # 如果图片获取失败，则退出
    if image1 is None or image2 is None:
        print("获取验证码图片失败，跳过此次破解。")
        return

    x = get_diff_location(image1, image2)
    # Geetest 实际滑动距离可能需要调整，这里减去一个偏移量（例如 6px）
    # 您需要根据实际测试来调整这个值
    x_track = x - 6
    print(f"计算缺口位置: {x}, 计划滑动距离: {x_track}")

    if x_track <= 0:
        print("计算出的滑动距离异常，跳过。")
        return

    tracks = get_track(x_track)

    ActionChains(driver).click_and_hold(slider_element).perform()
    for x_offset in tracks:
        ActionChains(driver).move_by_offset(xoffset=x_offset, yoffset=0).perform()

    # 稍微停顿一下，模拟人类操作
    time.sleep(random.uniform(0.1, 0.3))
    ActionChains(driver).release(slider_element).perform()

    # 等待验证结果
    time.sleep(3)


# ===================================================================
# 主执行流程 (合并后的)
# ===================================================================
if __name__ == '__main__':
    driver = None  # 初始化
    try:
        # --- 登录流程 (来自您的第二个脚本) ---
        driver = webdriver.Chrome(executable_path=ChromeDriverManager().install())
        driver.maximize_window()
        # 隐式等待在查找验证码时可能导致问题，我们主要使用显式等待
        # driver.implicitly_wait(10)
        wait = WebDriverWait(driver, 15)  # 定义一个全局显式等待

        target_url = "http://192.168.66.162:20080/login?redirect=%2Findex"
        driver.get(target_url)
        print(f"成功访问URL：{target_url}")

        username_input = wait.until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div/div[1]/form/div[1]/div/div/input'))
        )
        print("用户名输入框已加载完成")
        username_input.send_keys("bonss_medical_admin")

        password_input = wait.until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div/div[1]/form/div[2]/div/div/input'))
        )
        password_input.send_keys("admin123..")

        login_button = wait.until(
            EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div/div[1]/form/div[4]/div/button'))
        )
        login_button.click()
        print("点击登录按钮")

        # --- 验证码破解流程 (来自您的第一个脚本) ---
        print("等待验证码弹窗加载...")

        count = 5  # 尝试次数

        # 使用10秒的显式等待，而不是 time.sleep(4)
        captcha_wait = WebDriverWait(driver, 10)

        while count > 0:
            try:
                # 1. 等待滑块出现
                # !! 重要: 确认 'gt_slider_knob' 是您登录页面弹窗中的正确 class
                element = captcha_wait.until(
                    EC.element_to_be_clickable((By.XPATH, '//*[@id="app"]/div/div[2]/div/div[2]'))
                )
                print(f"验证码滑块已加载，开始第 {6 - count} 次尝试...")

                # 2. 调用破解函数
                solve_captcha(driver, element)

                # 3. 检查是否成功
                # !! 重要: 确认 'gt_ajax_tip gt_success' 是您登录页面弹窗中的正确 class
                succes = captcha_wait.until(
                    EC.presence_of_element_located((By.XPATH, '//div[@class="gt_ajax_tip gt_success"]'))
                )
                if succes:
                    print('恭喜你！识别成功...')
                    break  # 成功，跳出 while 循环

            except TimeoutException:
                # 10秒内未找到成功标志，说明失败了
                print('识别错误，准备重试...')
                count -= 1
                # 您可能需要在这里点击 "刷新" 按钮（如果存在的话）
                # try:
                #     refresh_button = driver.find_element(By.CLASS_NAME, 'gt_refresh_button') # 替换为实际的刷新按钮 class
                #     refresh_button.click()
                #     print("点击刷新按钮，等待2秒后重试...")
                #     time.sleep(2)
                # except Exception:
                #     print("未找到刷新按钮，直接重试。")
                #
                # 如果没有刷新按钮，Geetest 失败后滑块可能会自动重置，
                # 否则您可能需要点击弹窗上的 "x" 再重新点击登录

            except Exception as e:
                print(f'破解过程中发生异常: {e}')
                count -= 1

        if count == 0:
            print("已达到最大尝试次数，破解失败。")

        print("验证码流程结束，等待登录跳转...")
        time.sleep(5)  # 等待登录成功后的页面跳转
        print(f"当前 URL: {driver.current_url}")

    except Exception as e:
        print(f"脚本执行过程中发生意外错误: {e}")



    finally:
        if driver:
            print("关闭浏览器。")
            driver.quit()